#include "timer.h"

namespace learnco{

learnco::Logger::ptr g_logger = learnco::LoggerMgr::GetInstance()->getLogger("user");

bool Timer::Comparator::operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const {
    if(!lhs && !rhs){
        return false;
    }
    if(!lhs) {
        return true;
    }
    if(!rhs) {
        return false;
    }
    if(lhs->m_next < rhs->m_next) {
        return true;
    }
    if(rhs->m_next < lhs->m_next) {
        return false;
    }
    return lhs.get() < rhs.get();
}

Timer::Timer(uint64_t ms, std::function<void()>cb, bool recurring ,TimerManger* manager)
    :m_ms(ms)
    ,m_cb(cb)
    ,m_recurring(recurring)
    ,m_manager(manager){
    m_next = learnco::GetCurrentMS() + m_ms;
}


Timer::ptr TimerManger::addTimer(uint64_t ms, std::function<void()>cb, bool recurring){
    Timer::ptr timer(new Timer(ms, cb, recurring, this));
    RWMutexType::writeRaiiImpl LOCK(m_mutex);
    //insert 返回一个pair, first指向插入位置的迭代器，second为bool,代表是否插入
    auto it = m_timers.insert(timer).first;
    if((it == m_timers.begin()) && !m_tickled){
        //插入在头部，因此需要唤醒，重置定时时间
        m_tickled = true;
        v_onTimerInsertedAtFront();
    }
    return timer;
}


//获取离最近的定时任务 时间差，用于设置epoll_wait timeout
uint64_t TimerManger::getNextTime(){
    RWMutexType::readRaiiImpl LOCK(m_mutex);
    m_tickled = false;
    if(m_timers.empty()){
        return INT64_MAX;
    }

    const Timer::ptr& nexttimer = *(m_timers.begin());
    uint64_t now_ms = learnco::GetCurrentMS();
    if(now_ms >= nexttimer->m_next){
        return 0;
    }
    else{
        return nexttimer->m_next - now_ms;
    } 
}


//返回所有到期的任务
void TimerManger::listExpiredCb(std::vector<std::function<void()> >& cbs){
    uint64_t now_ms = learnco::GetCurrentMS();
    std::vector<Timer::ptr> expired;

    RWMutexType::readRaiiImpl LOCK(m_mutex);
    if(m_timers.empty()){
        return ;
    }
    LOCK.unlock();

    //找到最后一个到期任务
    RWMutexType::writeRaiiImpl LOCKW(m_mutex);
    auto it = m_timers.begin();
    while(it != m_timers.end()){

        if(now_ms < (*it)->m_next){
            break;
        }

        expired.push_back(*it);
        ++it;
    }
    

    //删除到期任务 earse[first, end)
    m_timers.erase(m_timers.begin(), it);

    cbs.reserve(expired.size());
    for(auto timer : expired){
        cbs.push_back(timer->m_cb);
        if(timer->m_recurring){
            timer->m_next = now_ms + timer->m_ms;
            m_timers.insert(timer);
        }
    }

}


}